package com.jiangyg.mall.admin.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jiangyg.mall.admin.constant.AdminConstant;
import com.jiangyg.mall.admin.constant.ColumnConstant;
import com.jiangyg.mall.admin.dao.DepartUserDao;
import com.jiangyg.mall.admin.dao.PermissionDao;
import com.jiangyg.mall.admin.dao.ResourceDao;
import com.jiangyg.mall.admin.dao.RoleUserDao;
import com.jiangyg.mall.admin.dto.ResourceDTO;
import com.jiangyg.mall.admin.entity.DepartUser;
import com.jiangyg.mall.admin.entity.Permission;
import com.jiangyg.mall.admin.entity.Resource;
import com.jiangyg.mall.admin.entity.RoleUser;
import com.jiangyg.mall.admin.enums.PermissionType;
import com.jiangyg.mall.admin.enums.ResourceType;
import com.jiangyg.mall.admin.vo.ResourceVO;
import com.jiangyg.mall.core.enums.Enable;
import com.jiangyg.mall.core.exception.BusinessException;
import com.jiangyg.mall.db.mybatis.BaseServiceImpl;
import com.jiangyg.mall.core.utils.I18n;
import com.jiangyg.mall.core.utils.bean.BeanUtils;
import com.jiangyg.mall.core.utils.tree.Tree;
import com.jiangyg.mall.core.utils.tree.TreeUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
public class ResourceServiceImpl extends BaseServiceImpl<ResourceDao, Resource> implements ResourceService {

    private final PermissionDao permissionDao;

    private final RoleUserDao roleUserDao;

    private final DepartUserDao departUserDao;

    @Autowired
    public ResourceServiceImpl(ResourceDao resourceDao,
                               RoleUserDao roleUserDao,
                               DepartUserDao departUserDao,
                               PermissionDao permissionDao) {
        super(resourceDao);
        this.roleUserDao = roleUserDao;
        this.permissionDao = permissionDao;
        this.departUserDao = departUserDao;
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<Tree> selectTree(String type, Long userId) throws Exception {
        // 1. 查询的资源类型判断
        final ResourceType[] types = StringUtils.equals(type, "1") ? ResourceType.MPI() : ResourceType.values();
        // 2. 用户所有的资源列表（如果是超级管理员则查询所有的菜单，用户ID传空即可）
        final List<Resource> resources = this.selectResourcesByUser(userId, null, types);
        // 3. 菜单结构化处理
        final List<Tree> menus = resources.stream().map(this::toMenu).collect(Collectors.toList());
        return TreeUtils.instance(menus).getTree();
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<ResourceVO> selectListByPid(Long pid, Long userId) throws Exception {
        final List<Resource> resources = this.selectResourcesByUser(userId, pid, ResourceType.R);
        return BeanUtils.transformList(resources, ResourceVO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveOrUpdate(ResourceDTO dto) throws Exception {
        // 如果类型为空，则默认请求类型
        if (StringUtils.isBlank(dto.getType())) {
            dto.setType(ResourceType.R.code);
        }
        // 节点ID处理
        if (dto.getPid() == null) {
            dto.setPid(AdminConstant.ROOT_ID);
        }
        return this.executeSaveOrUpdate(dto) > 0;
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public ResourceVO selectById(Long resourceId) throws Exception {
        final ResourceVO resource = this.executeSelectById(resourceId, ResourceVO.class);
        // 父级菜单名称
        if (resource != null) {
            if (resource.getPid().equals(AdminConstant.ROOT_ID.toString())) {
                resource.setPname("跟节点");
            } else {
                final Resource p = this.baseMapper.selectById(resource.getPid());
                resource.setPname(p.getName());
            }
        }
        return resource;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delete(List<Long> resourceIds) {
        // 判断要删除的节点是否有子节点
        final QueryWrapper<Resource> queryWrapper = new QueryWrapper<>();
        queryWrapper.in(ColumnConstant.PID, resourceIds);
        final Integer count = this.baseMapper.selectCount(queryWrapper);
        if (count > 0) {
            throw new BusinessException(I18n.getString("mall.admin.resource.delete.haveChild"));
        }
        // 资源是否授权
        final QueryWrapper<Permission> pwrapper = new QueryWrapper<>();
        pwrapper.in(ColumnConstant.REFERENCE_ID, resourceIds);
        final Integer count1 = this.permissionDao.selectCount(pwrapper);
        if (count1 > 0) {
            throw new BusinessException(I18n.getString("mall.admin.resource.delete.havePermission"));
        }
        return this.baseMapper.deleteBatchIds(resourceIds) > 0;
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public boolean checkHaveChildren(Long resourceId) {
        QueryWrapper<Resource> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(ColumnConstant.PID, resourceId);
        queryWrapper.eq(ColumnConstant.ENABLE, Enable.Y.scode);
        return this.baseMapper.selectCount(queryWrapper) > 0;
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED, readOnly = true)
    public List<String> selectGrantedAuthorities(Long userId) {
        final List<Resource> resources = this.selectResourcesByUser(userId, null, ResourceType.R);
        return resources.stream().map(Resource::getUrl).collect(Collectors.toList());
    }

    @Override
    public Map<String, Object> selectUserGrantedMenu(Long userId) throws Exception {
        // 查询用户所有的授权资源列表
        final List<Resource> resources = this.selectResourcesByUser(userId, null, ResourceType.values());
        // 生成菜单和权限代码列表
        final List<Tree> menus = new ArrayList<>();
        final List<String> authCodes = new ArrayList<>();
        resources.forEach(resource -> {
            switch (Objects.requireNonNull(ResourceType.decide(resource.getType()))) {
                case M:
                case P:
                case I:
                    menus.add(toMenu(resource));
                    break;
                case R:
                    if (StringUtils.isNotBlank(resource.getCode())) {
                        authCodes.add(resource.getCode());
                    }
                    break;
            }
        });
        TreeUtils utils = TreeUtils.instance(menus);
        final Map<String, Object> result = new HashMap<>();
        result.put("menus", utils.getTree());
        result.put("permissions", authCodes);
        return result;
    }

    /**
     * 功能描述：查询用户的资源列表
     *
     * @param userId 用户ID
     * @param pid    父级资源ID
     * @param types  资源类型
     * @return 结果
     */
    private List<Resource> selectResourcesByUser(Long userId, Long pid, ResourceType... types) {
        // 1. 没有指定资源类型，直接返回
        if (types == null || types.length == 0) {
            return Collections.EMPTY_LIST;
        }
        // 2. 如果指定用户则需要查询用户下的所有资源ID集合（权限类型：用户、角色、部门）
        List<Long> resourceIds = null;
        if (userId != null) {
            // 2.1 拼接用户权限条件
            final QueryWrapper<Permission> wrapper = new QueryWrapper<>();
            wrapper.or(w -> {
                w.eq(ColumnConstant.REFERENCE_ID, userId);
                w.eq(ColumnConstant.TYPE, PermissionType.U.code);
            });
            // 2.2 拼接角色权限条件
            final QueryWrapper<RoleUser> ruwrapper = new QueryWrapper<>();
            ruwrapper.eq(ColumnConstant.USER_ID, userId);
            final List<RoleUser> roleUsers = roleUserDao.selectList(ruwrapper);
            final List<Long> roleIds = roleUsers.stream().map(RoleUser::getRoleId).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(roleIds)) {
                wrapper.or(w -> {
                    w.in(ColumnConstant.REFERENCE_ID, roleIds);
                    w.eq(ColumnConstant.TYPE, PermissionType.R.code);
                });
            }
            // 2.3 拼接部门权限条件
            final QueryWrapper<DepartUser> duwrapper = new QueryWrapper<>();
            duwrapper.eq(ColumnConstant.USER_ID, userId);
            final List<DepartUser> departUsers = departUserDao.selectList(duwrapper);
            final List<Long> departIds = departUsers.stream().map(DepartUser::getDepartId).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(departIds)) {
                wrapper.or(w -> {
                    w.eq(ColumnConstant.REFERENCE_ID, departIds);
                    w.eq(ColumnConstant.TYPE, PermissionType.D.code);
                });
            }
            // 2.4. 执行查询用户所有的资源ID列表
            final List<Permission> permissions = this.permissionDao.selectList(wrapper);
            resourceIds = permissions.stream().map(Permission::getResourceId).distinct().collect(Collectors.toList());
        }
        // 3. 查询用户的资源列表
        final QueryWrapper<Resource> rwrapper = new QueryWrapper<>();
        // 3.1 如果资源列表不为空则拼接条件
        if (CollectionUtils.isNotEmpty(resourceIds)) {
            rwrapper.in(ColumnConstant.ID, resourceIds);
        }
        // 3.2 拼接指定的资源类型
        final List<String> typeList = Stream.of(types).map(t -> t.code).collect(Collectors.toList());
        rwrapper.in(ColumnConstant.TYPE, typeList);
        // 3.4 如果指定父级ID则拼接条件
        if (pid != null) {
            rwrapper.eq(ColumnConstant.PID, pid);
        }
        return this.baseMapper.selectList(rwrapper);
    }

    /**
     * 功能描述：资源转换为菜单对象
     *
     * @return 菜单对象
     */
    private Tree toMenu(Resource resource) {
        final Tree menu = new Tree();
        menu.setId(String.valueOf(resource.getId()));
        menu.setPid(String.valueOf(resource.getPid()));
        menu.setSort(resource.getSort());
        menu.setName(resource.getName());
        menu.addAttribute("url", resource.getUrl());
        menu.addAttribute("icon", resource.getIcon());
        menu.addAttribute("code", resource.getCode());
        return menu;
    }

}
